%{

#include <string>
#include <cstdlib>
#include <iostream>
using std::cout;
using std::endl;
using std::string;

#include "ast.hpp"
#include "parser.hpp"


#define Token(t) \
  return (yylval.token = t)

#define TokenStr(t) \
  yylval.str = new string(yytext, yyleng); return t

#define TokenStrLit(t) \
  yylval.str = new string(yytext+1, yyleng-2); \
  replaceAll(*yylval.str, "\\n", "\n");\
  replaceAll(*yylval.str, "\\\"", "\"");\
  replaceAll(*yylval.str, "\\0", "\0");\
  replaceAll(*yylval.str, "\\r", "\r");\
  replaceAll(*yylval.str, "\\t", "\t");\
  return t;

#define TokenInt(t) \
  yylval.token = read_int(string(yytext, yyleng).c_str(), yyleng); return t

#define TokenRStrId(n) \
  yylval.token = read_int(string(yytext+n, yyleng-n).c_str(), yyleng-n); \
  return TRegisterId

#define TokenRCodeId(id) \
  yylval.token = id; return TRegisterId


#define YY_USER_ACTION {yylloc.first_line = yylineno; }


extern "C" int yywrap() { }


int read_int(const char* s, int n)
{
  int pos = 0, base = 10, x = 0, v, sign = 1;

  if (s[0] == '-') { sign = -1; ++pos; }

  if (n > 1 && s[pos] == '0') {
    if (s[pos+1] == 'x' || s[pos+1] == 'X') { pos += 2; base = 16; }
    else if (s[pos+1] == 'b' || s[pos+1] == 'B') { pos += 2; base = 2; }
  }

  for (int i = n-1, f = 1; i > pos-1; --i, f *= base) {
    if (s[i] <= '9') v = s[i]-'0';
    else if (s[i] <= 'Z') v = s[i]-'A'+10;
    else v = s[i]-'a'+10;
    x += v*f;
  }

  return x*sign;
}

string& replaceAll(string& context, const string& from, const string& to)
{
    size_t lookHere = 0;
    size_t foundHere;
    while((foundHere = context.find(from, lookHere)) != string::npos)
    {
          context.replace(foundHere, from.size(), to);
          lookHere = foundHere + to.size();
    }
    return context;
}

//"$s8"|"$S8"        { TokenRCodeId(26); }
//"$s9"|"$S9"        { TokenRCodeId(27); }

%}


D     [0-9]
HD    [a-fA-F0-9]
BD    [01]
L     [a-zA-Z_]
ID    {L}({L}|{D})*
SIG   ("-")?


%%


("#")[^\n]*     { /* single line comment */ }


"."   { return '.'; }
":"   { return ':'; }
","   { return ','; }
"+"   { return '+'; }
"["   { return '['; }
"]"   { return ']'; }
";"   { return ';'; }
"@"   { return '@'; }


".ascii"|".ASCII"   { Token(Tascii); }
".asciiz"|".ASCIIZ" { Token(Tasciiz); }

".word"|".WORD"    { Token(Tword); }
".byte"|".BYTE"    { Token(Tbyte); }
".resw"|".RESW"    { Token(Tresw); }
".resb"|".RESB"    { Token(Tresb); }

".data"|".DATA"    { Token(Tdata); }
".text"|".TEXT"    { Token(Ttext); }

".start"|".START"   { Token(Tstart); }
".global"|".GLOBAL" { Token(Tglobal); }
".extern"|".EXTERN" { Token(Textern); }


".aps"|".APS"|"!"  { Token(TStatusFlag); }

"add"|"ADD"        { Token(Tadd); }
"addc"|"ADDC"      { Token(Taddc); }
"sub"|"SUB"        { Token(Tsub); }
"subc"|"SUBC"      { Token(Tsubc); }
"subr"|"SUBR"      { Token(Tsubr); }
"subrc"|"SUBRC"    { Token(Tsubrc); }

"and"|"AND"        { Token(Tand); }
"or"|"OR"          { Token(Tor); }
"xor"|"XOR"        { Token(Txor); }

"rll"|"RLL"        { Token(Trll); }
"rllc"|"RLLC"      { Token(Trllc); }
"rla"|"RLA"        { Token(Trla); }
"rlac"|"RLAC"      { Token(Trlac); }

"rrl"|"RRL"        { Token(Trrl); }
"rrlc"|"RRLC"      { Token(Trrlc); }
"rra"|"RRA"        { Token(Trra); }
"rrac"|"RRAC"      { Token(Trrac); }

"sll"|"SLL"        { Token(Tsll); }
"sllc"|"SLLC"      { Token(Tsllc); }
"sla"|"SLA"        { Token(Tsla); }
"slac"|"SLAC"      { Token(Tslac); }

"srl"|"SRL"        { Token(Tsrl); }
"srlc"|"SRLC"      { Token(Tsrlc); }
"sra"|"SRA"        { Token(Tsra); }
"srac"|"SRAC"      { Token(Tsrac); }

"ld"|"LD"          { Token(Tld); }
"st"|"ST"          { Token(Tst); }

"ldpri"|"LDPRI"    { Token(Tldpri); }
"ldpoi"|"LDPOI"    { Token(Tldpoi); }
"ldpod"|"LDPOD"    { Token(Tldpod); }

"stpri"|"STPRI"    { Token(Tstpri); }
"stpoi"|"STPOI"    { Token(Tstpoi); }
"stpod"|"STPOD"    { Token(Tstpod); }

"jmp"|"JMP"        { Token(Tjmp); }
"jmptr"|"JMPTR"    { Token(Tjmp); }
"jmpns"|"JMPNS"    { Token(Tjmpns); }
"jmpcs"|"JMPCS"    { Token(Tjmpcs); }
"jmpos"|"JMPOS"    { Token(Tjmpos); }
"jmpzs"|"JMPZS"    { Token(Tjmpzs); }
"jmpge"|"JMPGE"    { Token(Tjmpge); }
"jmpgt"|"JMPGT"    { Token(Tjmpgt); }
"jmpeq"|"JMPEQ"    { Token(Tjmpeq); }
"jmpfl"|"JMPFL"    { Token(Tjmpfl); }
"jmpnn"|"JMPNN"    { Token(Tjmpnn); }
"jmpnc"|"JMPNC"    { Token(Tjmpnc); }
"jmpno"|"JMPNO"    { Token(Tjmpno); }
"jmpnz"|"JMPNZ"    { Token(Tjmpnz); }
"jmplt"|"JMPLT"    { Token(Tjmplt); }
"jmple"|"JMPLE"    { Token(Tjmple); }
"jmpne"|"JMPNE"    { Token(Tjmpne); }

"sr"|"SR"          { Token(Tsr); }
"srtr"|"SRTR"      { Token(Tsr); }
"srns"|"SRNS"      { Token(Tsrns); }
"srcs"|"SRCS"      { Token(Tsrcs); }
"sros"|"SROS"      { Token(Tsros); }
"srzs"|"SRZS"      { Token(Tsrzs); }
"srge"|"SRGE"      { Token(Tsrge); }
"srgt"|"SRGT"      { Token(Tsrgt); }
"sreq"|"SREQ"      { Token(Tsreq); }
"srfl"|"SRFL"      { Token(Tsrfl); }
"srnn"|"SRNN"      { Token(Tsrnn); }
"srnc"|"SRNC"      { Token(Tsrnc); }
"srno"|"SRNO"      { Token(Tsrno); }
"srnz"|"SRNZ"      { Token(Tsrnz); }
"srlt"|"SRLT"      { Token(Tsrlt); }
"srle"|"SRLE"      { Token(Tsrle); }
"srne"|"SRNE"      { Token(Tsrne); }

"syscall"|"SYSCALL"  { Token(Tsyscall); }


[rR]{D}+             { TokenRStrId(1); }

[%$][rR]{D}+         { TokenRStrId(2); }

"$zero"|"$ZERO"      { TokenRCodeId(0); }
"$psw"|"$PSW"        { TokenRCodeId(1); }
"%psw"|"%PSW"        { TokenRCodeId(1); }
"$v0"|"$V0"          { TokenRCodeId(2); }

"$a0"|"$A0"          { TokenRCodeId(3); }
"$a1"|"$A1"          { TokenRCodeId(4); }
"$a2"|"$A2"          { TokenRCodeId(5); }
"$a3"|"$A3"          { TokenRCodeId(6); }
"$a4"|"$A4"          { TokenRCodeId(7); }

"$t0"|"$T0"          { TokenRCodeId(8); }
"$t1"|"$T1"          { TokenRCodeId(9); }
"$t2"|"$T2"          { TokenRCodeId(10); }
"$t3"|"$T3"          { TokenRCodeId(11); }
"$t4"|"$T4"          { TokenRCodeId(12); }
"$t5"|"$T5"          { TokenRCodeId(13); }
"$t6"|"$T6"          { TokenRCodeId(14); }
"$t7"|"$T7"          { TokenRCodeId(15); }
"$t8"|"$T8"          { TokenRCodeId(16); }
"$t9"|"$T9"          { TokenRCodeId(17); }

"$v1"|"$V1"          { TokenRCodeId(26); }
"$at"|"$AT"          { TokenRCodeId(27); }
"%tmp"|"%TMP"        { TokenRCodeId(27); }

"$s0"|"$S0"          { TokenRCodeId(18); }
"$s1"|"$S1"          { TokenRCodeId(19); }
"$s2"|"$S2"          { TokenRCodeId(20); }
"$s3"|"$S3"          { TokenRCodeId(21); }
"$s4"|"$S4"          { TokenRCodeId(22); }
"$s5"|"$S5"          { TokenRCodeId(23); }
"$s6"|"$S6"          { TokenRCodeId(24); }
"$s7"|"$S7"          { TokenRCodeId(25); }

"$ra"|"$RA"          { TokenRCodeId(28); }
"$fp"|"$FP"          { TokenRCodeId(29); }
"$sp"|"$SP"          { TokenRCodeId(30); }
"$pc"|"$PC"          { TokenRCodeId(31); }


{ID}                 { TokenStr(TIdentifier); }

{SIG}((0[xX]{HD}+)|(0[bB]{BD}+)|({D}+)) { TokenInt(TInteger); }

\"(\\.|[^\\"\n])*\"  { TokenStrLit(TString); }

\'(\\.|[^\\'\n])*\'  { TokenStrLit(TString); }

[ \t\v\n\r\f]        { /* whitespace */ }

.                    { cout << "Unknown caracter at line "
                            << yylineno << endl;
                       yyterminate(); }
